package repositoryimpl

import (
	"errors"
	"fmt"
	"strings"

	"github.com/astaxie/beego/orm"

	"cvevulner/cve-ddd/domain"
	"cvevulner/cve-ddd/domain/repository"
	"cvevulner/models"
)

func NewRepositoryImpl() repositoryImpl {
	return repositoryImpl{}
}

type repositoryImpl struct {
}

func (impl repositoryImpl) FindCves(opt repository.Option) (cves domain.Cves, err error) {
	var data []CveInfo

	sql := `select a.cve_num,a.cve_version,b.openeuler_score,b.openeuler_vector,b.issue_num,
       b.repo,b.abi_version,b.owned_component, b.cve_brief, b.cve_level, b.affected_version,
       c.introduction, c.summary, c.description, c.affect_product, c.will_fix_product, c.reference_link,c.theme
from cve_vuln_center a
join cve_issue_template b on a.cve_id=b.cve_id
join cve_security_notice c on a.cve_id=c.cve_id
where a.cve_num in (%s)
and a.cve_status = 2
and a.organizate_id = 1
and b.status in (1,2,3,5)
`
	if opt.Component != "" {
		sql += fmt.Sprintf(`and a.pack_name = "%s"`, opt.Component)
	}

	o := orm.NewOrm()
	cveStr := "\"" + strings.Join(opt.CveNum, "\",\"") + "\""
	if _, err = o.Raw(fmt.Sprintf(sql, cveStr)).QueryRows(&data); err != nil {
		return
	}

	for _, v := range data {
		affect := v.GetAffectProduct()

		cve := domain.Cve{
			Component:       v.OwnedComponent,
			ComponentDesc:   v.GetComponentDesc(),
			SeverityLevel:   v.CveLevel,
			AffectedVersion: strings.Split(affect, "/"),
			AffectedProduct: affect,
			OpeneulerScore:  v.OpeneulerScore,
			Theme:           v.Theme,
			CveNum:          v.CveNum,
			CveBrief:        v.CveBrief,
			OpeneulerVector: v.OpeneulerVector,
			CveVersion:      v.CveVersion,
			AbiVersion:      v.AbiVersion,
			ColdIssue: domain.Issue{
				Number: v.IssueNum,
				Repo:   v.Repo,
			},
		}

		cves = append(cves, cve)
	}

	return
}

func (impl repositoryImpl) MaxBulletinID() (string, error) {
	var saFileRecord models.SaFileRecord

	o := orm.NewOrm()
	err := o.QueryTable(&saFileRecord).
		Filter("sa_type", models.SaFileRecordHotPatch).
		OrderBy("-file_name").
		One(&saFileRecord)
	if errors.Is(err, orm.ErrNoRows) {
		return "", nil
	}

	if err != nil {
		return "", err
	}

	return saFileRecord.FileName, nil
}

type list struct {
	models.IssueTemplate
	AffectProduct  string `orm:"column(affect_product)"`
	WillFixProduct string `orm:"column(will_fix_product)"`
}

// 是否是新增了分析说明模块的数据
func (l list) IsIssueWithAnalysis() bool {
	return l.WillFixProduct != ""
}

func (l list) GetAffectProduct() []string {
	if l.IsIssueWithAnalysis() {
		return strings.Split(l.WillFixProduct, "/")
	} else {
		return strings.Split(l.AffectProduct, "/")
	}
}

var statusMap = map[int8]string{
	1: "待办的",
	2: "进行中",
	3: "已完成",
}

func (impl repositoryImpl) GetAllIssue() (data domain.CollectedDataSlice, err error) {
	sql := `select a.*, b.affect_product, b.will_fix_product from cve_issue_template a
    join cve_security_notice b on a.cve_id=b.cve_id
    where a.cve_id in (select cve_id from cve_vuln_center where cve_status = 2 and is_export in (0,3) and organizate_id = 1) 
    and a.status in (1,2,3,5)
`

	var issueTemp []list
	_, err = orm.NewOrm().Raw(sql).QueryRows(&issueTemp)
	if err != nil {
		return
	}

	for _, v := range issueTemp {
		t := domain.CollectedData{
			Issue: domain.Issue{
				Number: v.IssueNum,
				Status: statusMap[v.Status],
				Repo:   v.Repo,
			},
			Id:              v.TemplateId,
			CveNum:          v.CveNum,
			Score:           v.OpenEulerScore,
			Version:         v.OwnedVersion,
			AffectedProduct: v.GetAffectProduct(),
			CreateTime:      v.CreateTime,
		}

		data = append(data, t)
	}

	return
}

func (impl repositoryImpl) SetIgnoreStatus(id int64) error {
	sql := fmt.Sprintf("update cve_issue_template set is_ignore = 1 where template_id = %d", id)
	_, err := orm.NewOrm().Raw(sql).Exec()

	return err
}

func (impl repositoryImpl) GetAllPackage() (list []string, err error) {
	sql := "select package_name from cve_git_package_info"

	var info []models.GitPackageInfo
	_, err = orm.NewOrm().Raw(sql).QueryRows(&info)
	if err != nil {
		return
	}

	for _, v := range info {
		list = append(list, v.PackageName)
	}

	return
}
